﻿using System;
using System.Linq;
using System.Linq.Expressions;

namespace OS.Infrastructure.Domain
{
    public interface ISpecification<T> where T : class
    {
        Expression<Func<T, bool>> IsSatisfiedBy();
        bool Equals(Specification<T> other);
        bool Equals(object obj);
        int GetHashCode();
    }

    public abstract class Specification<T> : ISpecification<T>  where T:class
    {
        protected virtual object[] Parameters
        {
            get { return new object[] {Guid.NewGuid()}; }
        }

        #region ISpecification<T> Members

        public abstract Expression<Func<T, bool>> IsSatisfiedBy();

        public bool Equals(Specification<T> other)
        {
            return Parameters.SequenceEqual(other.Parameters);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((Specification<T>) obj);
        }

        public override int GetHashCode()
        {
            return Parameters.GetHashCode();
        }

        #endregion

        public static Specification<T> operator &(Specification<T> spec1, Specification<T> spec2)
        {
            return new SpecificationAnd<T>(spec1, spec2);
        }

        public static bool operator false(Specification<T> spec1)
        {
            return false;
        }

        public static bool operator true(Specification<T> spec1)
        {
            return false;
        }

        public static Specification<T> operator |(Specification<T> spec1, Specification<T> spec2)
        {
            return new SpecificationOr<T>(spec1, spec2);
        }

        public static Specification<T> operator !(Specification<T> spec1)
        {
            return new SpecificationNegate<T>(spec1);
        }
    }
}